﻿/*
 *   BuiltinFunctions.cs
 *   Copyright (c) 2011 - BitFox Software and Ricardo Federico Villafañes
 *   Contact: federvillaf@hotmail.com
 *   
 *   Licensing: This product is covered by Microsoft Reciprocal License (MS-RL)
 *              see https://bitfoxeval.codeplex.com/license
*/

using System;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics.CodeAnalysis;

namespace BitFox.Evaluator.Functions
{
    /// <summary>
    /// This static class is in charge of handling registration of those functions that will be available for evaluator.
    /// </summary>
    /// <remarks>For a function be available for evaluator, it must be PUBLIC and STATIC.</remarks>
    [SuppressMessage("Microsoft.Performance", "CA1810")]
    public static class BuiltinFunctions
    {
        static object locker = new object();
        static Dictionary<string, int> funcList;

        /// <summary>
        /// The collection of declared functions.
        /// </summary>
        internal static Dictionary<string, FunctionDescriptor> Functions = 
            new Dictionary<string,FunctionDescriptor>();

        /// <summary>
        /// For examination purposes, the list of functions actually used by evaluator.
        /// </summary>
        public static Dictionary<string, int> FunctionList
        {
            get
            {
                lock (locker)
                {
                    if (funcList == null)
                        funcList = new Dictionary<string, int>();
                }
                return funcList;
            }
        }

        /// <summary>
        /// Helper method that register a function for the evaluator, given its 'MethodInfo'
        /// </summary>
        /// <param name="mi">The method that corresponds to the fox's function to register for evaluator.</param>
        public static void RegisterFunction(MethodInfo mi)
        {
            var desc = new FunctionDescriptor(mi);
            string nargs = desc.ArgumentCount.FoxToString();
            FunctionList[desc.Name] = desc.ArgumentCount;
            Functions[desc.Name + nargs] = desc;

            // VFP can identify a function using short-names, up to 4 characters long.
            // If, when the name is shortened, the algo detects that this short-name is already used, it removes
            // any reference to this short-name. This is done because we have to avoid ambiguities between function's names.
            for (int il = desc.Name.Length - 1; il >= 4; il--)
            {
                string shname = desc.Name.Substring(0, il);
                if (Functions.ContainsKey(shname + nargs))
                    Functions.Remove(shname + nargs);
                else
                    Functions[shname + nargs] = desc;
            }
        }

        /// <summary>
        /// Add all the suitable methods of a type to the list of declared functions, that will be available for the evaluator.
        /// </summary>
        /// <param name="typeWithMethods">The type that contains methods</param>
        public static void AddFunctionsFromType(Type typeWithMethods)
        {
            if (typeWithMethods == null) throw new ArgumentNullException("typeWithMethods");
            foreach (var mi in typeWithMethods.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Static))
                RegisterFunction(mi);
        }

        /// <summary>
        /// Static constructor. Add all standard 'builtin' functions of foxpro.
        /// </summary>
        [SuppressMessage("Microsoft.Performance", "CA1810")]
        static BuiltinFunctions()
        {
            AddFunctionsFromType(typeof(ConvertionFunctions));
            AddFunctionsFromType(typeof(StringFunctions));
            AddFunctionsFromType(typeof(NumericFunctions));
            AddFunctionsFromType(typeof(ArrayFunctions));
            AddFunctionsFromType(typeof(DateTimeFunctions));
            AddFunctionsFromType(typeof(MiscFunctions));
        }
    }
}
